<template>
    <div class="box box-primary">
        <div class="box-header">
            <h4 class="text-primary text-center">设备树</h4>
        </div>
        <div class="box-body">
            <div class="form-inline" autocomplete="off" spellcheck="false">
                <div class="form-group form-group-sm">
                    <label>搜索</label>
                    <input type="text" class="form-control" placeholder="关键字" v-model.trim="q" @keydown.enter.prevent ref="q">
                </div>
                <span class="hidden-xs">&nbsp;&nbsp;</span>
                <div class="form-group pull-right">
                    <router-link :to="`/devices/1`" class="btn btn-default btn-sm">
                        <i class="fa fa-table"></i> 列表视图
                    </router-link>
                </div>
            </div>
            <br>
            <div class="clearfix"></div>
            <div class="content">
                <div :class="[{'col-md-3': hasAnyRole(serverInfo, userInfo, '管理员', '操作员'), 'col-md-4': !hasAnyRole(serverInfo, userInfo, '管理员', '操作员')}]">
                    <ul class="nav nav-tabs">
                        <li class="active"><a href="#dev-tree-wrapper" data-toggle="tab">设备树</a></li>
                        <li><a href="#group-tree-wrapper" data-toggle="tab">分 组</a></li>
                    </ul>
                    <div class="tab-content" style="margin: 10px 0;" id="tab-tree-wrapper">
                        <div class="tab-pane active" ref="devTreeWrapper" id="dev-tree-wrapper">
                            <el-tree ref="devTree" id="dev-tree" node-key="key" v-if="showTree" :style="`${isMobile() ? 'max-height:200px;' : ''};min-height:200px;overflow:auto;`"
                                :props="treeProps" :load="treeLoad" :filter-node-method="treeFilter" lazy
                                @node-click="treeNodeClick" @node-contextmenu="treeNodeRightClick" >
                                <span class="custom-tree-node" slot-scope="{node, data}">
                                    <span :class="{'text-green': treeLeaf(data) && data.status === 'ON'}">
                                        <i :class="['fa', {
                                            'fa-sitemap' : !treeLeaf(data),
                                            'fa-camera': treeLeaf(data)
                                        }]"></i>
                                        <span class="ellipsis" :title="node.label">{{node.label}}</span>
                                    </span>
                                </span>
                            </el-tree>
                        </div>
                        <div class="tab-pane" ref="groupTreeWrapper" id="group-tree-wrapper">
                            <el-tree ref="groupTree" id="group-tree" node-key="key" v-if="showGroupTree" :style="`${isMobile() ? 'max-height:200px;' : ''};min-height:200px;overflow:auto;`"
                                :props="treeProps" :load="groupTreeLoad" :filter-node-method="treeFilter" lazy
                                @node-click="treeNodeClick" @node-contextmenu="treeNodeRightClick" >
                                <span class="custom-tree-node" slot-scope="{node, data}">
                                    <span :class="{'text-green': data.status === 'ON' && data.subCount === 0 && data.code && data.serial && !data.custom}">
                                        <i :class="['fa', {
                                            'fa-home': !data.code,
                                            'fa-sitemap' : data.code && !treeLeaf(data),
                                            'fa-camera': treeLeaf(data)
                                        }]"></i>
                                        <span class="ellipsis" :title="node.label">{{node.label}}</span>
                                    </span>
                                </span>
                            </el-tree>
                        </div>
                    </div>
                </div>
                <VueContextMenu class="right-menu" :target="contextMenuTarget" :show="contextMenuVisible" @update:show="(show) => contextMenuVisible = show">
                    <a role="button" @click="treeRefresh" v-show="contextMenuNodeData">
                        <i class="fa fa-refresh"></i> 刷新节点
                    </a>
                    <a role="button" @click="showNodeAddDlg" v-show="contextMenuNodeData && contextMenuNodeData.custom">
                        <i class="fa fa-plus"></i> 新建节点
                    </a>
                    <a role="button" @click="showCustomListDlg" v-show="contextMenuNodeData && contextMenuNodeData.custom && contextMenuNodeData.code">
                        <i class="fa fa-check"></i> 选择通道
                    </a>
                    <a role="button" @click="showNodeEditDlg" v-show="contextMenuNodeData && !(contextMenuNodeData.custom && !contextMenuNodeData.code)">
                        <i class="fa fa-edit"></i> 编辑节点
                    </a>
                    <a role="button" @click="removeCustomNode" v-show="contextMenuNodeData && contextMenuNodeData.custom && contextMenuNodeData.code">
                        <i class="fa fa-remove"></i> 删除节点
                    </a>
                </VueContextMenu>
                <div id="dev-tree-right" :class="['row', {'col-md-9': hasAnyRole(serverInfo, userInfo, '管理员', '操作员'), 'col-md-8': !hasAnyRole(serverInfo, userInfo, '管理员', '操作员')}]" data-sticky-wrap data-sticky-for="1000" data-sticky-class="sticky">
                    <div class="col-md-9 col-lg-10" id="dev-tree-player">
                        <div class="view-list row">
                            <div class="video-show">
                                <div>
                                    <div class="no-margin no-padding video col-sm-6" @click="clickPlayer(player, index, $event)" :class="{'active': index == playerIdx, 'col-sm-12': playersLength == 1,'col-sm-6': playersLength == 4,'col-sm-4': playersLength == 9,'col-sm-3': playersLength == 16}" v-for="(player,index) in players" :key="index" @mousemove="resetCloseTimer(player)" @touchstart="resetCloseTimer(player)">
                                        <LivePlayer :videoUrl="player.url" :poster="player.poster" live muted stretch digitalZoom
                                            @message="$message" :hideBigPlayButton="!!serverInfo.HideBigPlayButton"
                                            v-loading="player.bLoading" :loading.sync="player.bLoading" element-loading-text="加载中..." element-loading-background="#000">
                                        </LivePlayer>
                                        <div class="video-close" v-show="player.url && player.bCloseShow" v-on:click="closeVideo(player)">关闭</div>
                                    </div>
                                </div>
                            </div>
                        </div>
                        <br>
                        <br>
                    </div>
                    <div class="col-md-3 col-lg-2" id="dev-tree-ptz" v-if="hasAnyRole(serverInfo, userInfo, '管理员', '操作员')">
                        <div class="ptz-block">
                            <div class="ptz-cell ptz-up" :class="{'readonly': !playing }" command="up" title="上" @mousedown.prevent="ptzControl">
                                <i class="fa fa-chevron-up"></i>
                            </div>
                            <div class="ptz-cell ptz-left" :class="{'readonly': !playing }" command="left" title="左" @mousedown.prevent="ptzControl">
                                <i class="fa fa-chevron-left"></i>
                            </div>
                            <div class="ptz-cell ptz-center" title="云台控制">
                                <i class="fa fa-microphone" :class="{'readonly': !playing }" title="按住喊话" @mousedown.prevent="talkStart" v-if="canTalk() && serverInfo.VersionType != '标准版'"></i>
                                <i class="fa fa-microphone-slash" :class="{'readonly': !playing }" title="由于浏览器安全策略, 非 HTTPS 或 localhost 访问, 对讲不可用" v-if="!canTalk() && serverInfo.VersionType != '标准版'"></i>
                            </div>
                            <div class="ptz-cell ptz-right" :class="{'readonly': !playing }" command="right" title="右" @mousedown.prevent="ptzControl">
                                <i class="fa fa-chevron-right"></i>
                            </div>
                            <div class="ptz-cell ptz-down" :class="{'readonly': !playing }" command="down" title="下" @mousedown.prevent="ptzControl">
                                <i class="fa fa-chevron-down"></i>
                            </div>
                            <div class="ptz-cell ptz-zoomin" :class="{'readonly': !playing }" command="zoomin" title="放大" @mousedown.prevent="ptzControl">
                                <i class="fa fa-plus"></i>
                            </div>
                            <div class="ptz-cell ptz-zoomout" :class="{'readonly': !playing }" command="zoomout" title="缩小" @mousedown.prevent="ptzControl">
                                <i class="fa fa-minus"></i>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="clearfix"></div>
                <div class="text-center text-gray" v-if="isDemoUser(serverInfo, userInfo)">
                    <br>
                    提示: 演示系统限制匿名登录播放时间, 若需测试长时间播放, 请<a target="_blank" href="//www.liveqing.com/docs/download/LiveGBS.html">下载使用</a>
                </div>
                <br>
            </div>
        </div>
        <!-- <div class="box-footer">
        </div> -->
        <DeviceTreeNodeEditDlg ref="nodeEditDlg" @submit="treeRefresh" style="z-index:2001;"></DeviceTreeNodeEditDlg>
        <ChannelCustomListDlg ref="customListDlg" @hide="treeRefresh" style="z-index:2001;"  size="modal-lg" :title="customListDlgTitle"></ChannelCustomListDlg>
        <!-- <resize-observer @notify="resetTreeMaxHeight"/> -->
    </div>
</template>

<script>
import 'assets/js/jquery-sticky.js';
import _ from "lodash";
import { mapState } from "vuex"
import LivePlayer from '@liveqing/liveplayer'
import { component as VueContextMenu } from '@penggy/vue-context-menu'
import DeviceTreeNodeEditDlg from 'components/DeviceTreeNodeEditDlg.vue'
import ChannelCustomListDlg from 'components/ChannelCustomListDlg.vue'
export default {
	props: {},
	data() {
		return {
			q: "",
			loading: false,
			showTree: true,
			showGroupTree: true,
			playerIdx: 0,
			playersLength: 4,
			players: [],
			pnode: null,
			contextMenuTarget: null,
			contextMenuVisible: false,
			contextMenuNodeData: null,
			treeProps: {
				label: (data, node) => {
					node.serial = data.serial;
					node.code = data.code;
					var label = (data.customName || data.name || data.id);
					if(!this.treeLeaf(data) && data.subCount > 0) {
						label += ` [${data.onlineSubCount}/${data.subCount}]`;
					}
					return label;
				},
				isLeaf: (data, node) => {
					return this.treeLeaf(data);
				},
				disabled: (data, node) => {
					return this.treeLeaf(data) && data.status != "ON";
				}
			},
			customListDlgTitle: "选择通道",
			sticky: null,
		};
	},
	components: {
		LivePlayer, VueContextMenu, DeviceTreeNodeEditDlg, ChannelCustomListDlg
	},
	computed: {
		...mapState(['userInfo', 'serverInfo']),
		playing() {
			var player = this.players[this.playerIdx]||{};
			return !!player.url;
		},
		canPTZ() {
			var player = this.players[this.playerIdx]||{};
			return !!player.url && (player.ptzType === 0 || player.ptzType === 1);
		}
	},
	mounted() {
		this.setPlayersLength(this.playersLength);
		this.contextMenuTarget = document.querySelector('#tab-tree-wrapper');
		$(document).on("mouseup touchend", this.ctrlStop);
		$(document).on('expanded.pushMenu collapsed.pushMenu', this.updateSticky);
		$("#dev-tree-right").sticky({ topSpacing: 40, bottomSpacing: 80 })
	},
	beforeDestroy() {
		this.ctrlStop();
		$(document).off("mouseup touchend", this.ctrlStop);
		$(document).off('expanded.pushMenu collapsed.pushMenu', this.updateSticky);
		this.clearVideos();
		$("#dev-tree-right").unstick();
	},
	beforeRouteLeave(to, from, next) {
		this.ctrlStop();
		this.clearVideos();
		next();
	},
	beforeRouteUpdate(to, from, next) {
		this.ctrlStop();
		this.clearVideos();
		next();
	},
	watch: {
		q: function(newVal, oldVal) {
			this.$refs['devTree'].filter(newVal);
			this.$refs['groupTree'].filter(newVal);
		},
	},
	methods: {
		treeLoad(data, resolve) {
			var serial = data.serial||"";
			var pcode = data.code||"";
			$.get("/api/v1/device/channeltree", {
				serial: serial,
				pcode: pcode
			}).then(ret => {
				resolve((ret||[]).map(v => {
					return Object.assign(v, {
						key: v.id,
					})
				}));
				this.$refs['devTree'].filter(this.q);
			})
		},
		groupTreeLoad(data, resolve) {
			var serial = data.serial||"";
			var pcode = data.code||"";
			$.get("/api/v1/device/grouptree", {
				serial: serial,
				pcode: pcode
			}).then(ret => {
				resolve((ret||[]).map(v => {
					return Object.assign(v, {
						key: v.id,
					})
				}));
				this.$refs['groupTree'].filter(this.q);
			})
		},
		treeFilter(value, data) {
			 if (!value) return true;
			 return data.name.indexOf(value) !== -1 || data.customName.indexOf(value) !== -1 || data.id.indexOf(value) !== -1;
		},
		treeNodeRefresh(key) {
			let node = this.$refs['devTree'].getNode(key);
			if(!node) return;
			node.loaded = false;
			node.expand();
		},
		treeNodeRightClick(event, data, node, c) {
			if(!this.hasAnyRole(this.serverInfo, this.userInfo, '管理员')) return;
			this.contextMenuNodeData = data;
			this.pnode = node.parent;
			var new_event;
			if(typeof(MouseEvent) === 'function') {
                new_event = new MouseEvent(event.type, event);
			} else {
                new_event = document.createEvent('MouseEvents');
                new_event.initMouseEvent(event.type, true, true, event.view, event.detail,
                    event.screenX, event.screenY, event.clientX, event.clientY,
                    event.ctrlKey, event.altKey, event.shiftKey, event.metaKey, event.button, event);
			}
			this.contextMenuTarget.dispatchEvent(new_event);
		},
		treeNodeClick(data, node, c) {
			this.contextMenuNodeData = null;
			if(this.treeLeaf(data) && data.status === "ON") {
				var player = this.players[this.playerIdx]||{};
				if(player.bLoading) return;
				this.closeVideo(player);
				player.serial = data.serial;
				player.code = data.code;
				player.ptzType = data.ptzType;
				player.bLoading = true;
				$.get("/api/v1/stream/start", {
					serial: data.serial,
					code: data.code,
				}).then(streamInfo => {
					streamInfo = streamInfo || {};
					var videoUrl = streamInfo.HLS;
					var protocol = "HLS";
					if(this.flvSupported()) {
						if(streamInfo.WS_FLV) {
							videoUrl = streamInfo.WS_FLV;
							protocol = "WS_FLV";
						} else if(streamInfo.FLV) {
							videoUrl = streamInfo.FLV;
							protocol = "FLV";
						}
					}
					player.protocol = protocol;
					player.poster = protocol == "RTMP" ? "" : streamInfo.SnapURL;
					this.$nextTick(() => {
						player.url = videoUrl || "";
					});
				}).fail(() => {
					this.$nextTick(() => {
						player.bLoading = false;
					})
				})
				this.setPlayerIdx(this.playerIdx + 1);
			}
		},
		showNodeEditDlg() {
			this.contextMenuVisible = false;
			var data = Object.assign({}, this.contextMenuNodeData, { parent: this.getParentData(), add: false});
			this.$refs['nodeEditDlg'].show(data);
		},
		showNodeAddDlg() {
			this.contextMenuVisible = false;
			var data = Object.assign({
				serial: this.contextMenuNodeData.serial||'',
				code: '',
				name: '',
				customName: '',
			}, { parent: this.contextMenuNodeData, add: true});
			this.$refs['nodeEditDlg'].show(data);
		},
		showCustomListDlg() {
			this.contextMenuVisible = false;
			this.customListDlgTitle = `选择通道(${this.contextMenuNodeData.customName || this.contextMenuNodeData.name || this.contextMenuNodeData.code})`;
			this.$refs['customListDlg'].show(this.contextMenuNodeData.code);
		},
		removeCustomNode() {
			this.contextMenuVisible = false;
			if(!this.contextMenuNodeData) return;
			var name = this.contextMenuNodeData.customName || this.contextMenuNodeData.name || this.contextMenuNodeData.id;
			this.$confirm(`确认删除 ${name}`, "提示", {
				lockScroll: false,
			}).then(() => {
				$.get("/api/v1/channel/remove", {
					serial: this.contextMenuNodeData.serial,
					code: this.contextMenuNodeData.code,
				}).always(() => {
					this.treeNodeRefresh(this.pnode);
				});
			}).catch(() => {});
		},
		treeRefresh() {
			this.contextMenuVisible = false;
			if(this.pnode) {
				this.treeNodeRefresh(this.pnode);
				return;
			}
			this.showTree = false;
			this.$nextTick(() => {
				this.showTree = true;
			})
		},
		treeLeaf(data) {
			if (data && data.subCount == 0 && data.serial && data.code && !data.custom) {
				if (this.serverInfo.StrictChannelParental && data.parental) return false;
				if (this.serverInfo.StrictChannelCode && data.code.length != 20) return false;
				if (!data.parental || data.manufacturer != 'LiveQing') {
					// if (data.code.length != 20 || (data.code.substring(10, 13) != "216" && data.code.substring(10, 13) != "215" && data.code.substring(10, 13) != "200")) {
					if (data.code.length != 20 || (data.code.substring(10, 13) != "216" && data.code.substring(10, 13) != "215")) {
						return true;
					}
				}
			}
			return false;
		},
		getParentData() {
			if(!this.contextMenuNodeData) return null;
			if(!this.$refs["devTree"]) return null;
			var pNode = this.$refs["devTree"].getNode(this.contextMenuNodeData);
			if(!pNode) return null;
			if(!pNode.parent) return null;
			return pNode.parent.data;
		},
		resetCloseTimer(player) {
			if(!player) return;
			player.bCloseShow = true;
			if (player.closeTimer) {
				clearTimeout(player.closeTimer)
			}
			player.closeTimer = setTimeout(() => {
				player.bCloseShow = false;
			}, 2000);
		},
		closeVideo(player) {
			if(!player) return;
			player.bLoading = false;
			player.bCloseShow = false;
			player.poster = "";
			player.url = "";
		},
		clearVideos() {
			for(var idx in this.players) {
				this.closeVideo(this.players[idx]);
			}
		},
		setPlayersLength(len) {
			if(len == this.players.length) {
				return
			}
			this.clearVideos();
			this.players = [];
			this.playersLength = len;
			for(var i=0; i < len; i++) {
				this.players.push({
					serial: "",
					code: "",
					url: "",
					protocol: "",
					poster: "",
					bLoading: false,
					bCloseShow: false,
				})
			}
		},
		setPlayerIdx(idx) {
			this.ctrlStop();
			this.playerIdx = idx % this.players.length;
		},
		clickPlayer(player, idx, event) {
			if(!$(event.target).hasClass("video-close")) {
				this.setPlayerIdx(idx);
			}
		},
		ptzControl(e) {
			var player = this.players[this.playerIdx];
			if(!player || !player.serial || !player.code) return;
			var $target = $(e.currentTarget);
			$.get("/api/v1/control/ptz", {
				serial: player.serial,
				code: player.code,
				command: $target.attr("command")
			});
			$target.addClass("active");
		},
		ptzStop() {
			var player = this.players[this.playerIdx];
			if(!player || !player.serial || !player.code) return;
			if ($(this.$el).find(".ptz-cell.active").length > 0) {
				$.get("/api/v1/control/ptz", {
					serial: player.serial,
					code: player.code,
					command: "stop"
				});
				$(this.$el).find(".ptz-cell.active").removeClass("active");
			}
		},
		talkStart(e) {
			if(!this.ws) {
				var talkURL = this.wsTalkURL();
				if(!talkURL) return;
				var ws = new WebSocket(talkURL);
				ws.onopen = evt => {
					console.log("ws talk open");
					this.ws = ws;
				}
				ws.onclose = evt => {
					console.log("ws talk close");
				}
				ws.onerror = evt => {
					console.log("ws talk error", evt);
				}
			}
			var $target = $(e.currentTarget);
			if(this.recorder) {
				$target.addClass("active");
				this.recorder.start();
				return;
			}
			LiveRecorder.get((rec, err) => {
				if(err) {
					alert(err);
					return
				}
				// this.$refs["player"].setMuted(true);
				$target.addClass("active");
				this.recorder = rec;
				this.recorder.start();
			}, {
				sampleBits: 16,
				sampleRate: 8000,
				pcmCallback: pcm => {
					// binary to base64 string
					var reader = new window.FileReader();
					reader.onloadend = () => {
						var base64 = reader.result;
						var base64 = base64.split(',')[1];
						if(this.ws) {
							this.ws.send(base64);
						}
					}
					reader.readAsDataURL(pcm);
				}
			})
		},
		talkStop() {
			if(this.recorder) {
				this.recorder.stop();
				// this.recorder = null;
				$(this.$el).find(".fa-microphone.active").removeClass("active");
				// this.$refs["player"].setMuted(false);
			}
			if(this.ws) {
				this.ws.close();
				this.ws = null;
			}
		},
		ctrlStop() {
			this.talkStop();
			this.ptzStop();
		},
		wsTalkURL() {
			var player = this.players[this.playerIdx];
			if(!player || !player.serial || !player.code) return '';
			var protocol = "ws:";
			if(location.protocol.startsWith("https")) {
				protocol = "wss:";
			}
			//url query param "format=pcm|g711"
			return `${protocol}//${location.host}/api/v1/control/ws-talk/${player.serial}/${player.code}?format=pcm`;
		},
		resetTreeMaxHeight() {
			var $tree = $("#dev-tree, #group-tree");
			if(this.isMobile()) {
				$tree.css("max-height", 200);
			} else {
				this.$nextTick(() => {
					var ph = $("#dev-tree-player").height() - 90;
					var wh = $(window).height() - 450;
					var th = wh > ph ? wh : ph;
					$tree.css("max-height", th);
				})
			}
		},
		updateSticky() {
            $("#dev-tree-right").sticky("update");
		},
	}
};
</script>

<style lang="less" scoped>
.right-menu {
	position: fixed;
	background: #fff;
	border: solid 1px rgba(0, 0, 0, .2);
	border-radius: 3px;
	z-index: 999;
	display: none;
}
.right-menu a {
	width: 100px;
	height: 28px;
	line-height: 28px;
	text-align: center;
	display: block;
	color: #1a1a1a;
}
.right-menu a:hover {
	background: #eee;
	color: #fff;
}
.right-menu {
    border: 1px solid #eee;
    box-shadow: 0 0.5em 1em 0 rgba(0,0,0,.1);
    border-radius: 1px;
}
a {
    text-decoration: none;
}
.right-menu a {
    padding: 2px;
}
.right-menu a:hover {
    background: #42b983;
}
.video {
	position: relative;

	.video-close {
		position: absolute;
		top: 5px;
		right: 5px;
		color: white;
		font-size: 12px;
		background-color: fade(gray, 50%);
		padding: 2px 5px;
		cursor: pointer;
		border-radius: 2px;
		max-width: 120px;
		overflow: hidden;
		white-space: nowrap;
		text-overflow: ellipsis;
	}
}
.ptz-block {
	width: 150px;
	height: 180px;
	margin: 0 auto;
	text-align: center;
	position: relative;
	font-size: 24px;
}

.ptz-cell {
	width: 50px;
	height: 50px;
	line-height: 50px;
	position: absolute;
}

.ptz-cell.active {
	color: #ccc;
	font-size: 26px;
}
.ptz-cell.readonly {
	color: #ddd;
}
.fa-microphone.active {
	color: #FFF;
}
.fa-microphone.readonly {
	color: #ddd;
}
.fa-microphone-slash.readonly {
	color: #ddd;
}

.ptz-center {
	top: 50px;
	left: 50px;
	border-radius: 25px;
	background-color: #ccc;
}

.ptz-up {
	top: 0;
	left: 50px;
}

.ptz-left {
	top: 50px;
	left: 0;
}

.ptz-right {
	top: 50px;
	left: 100px;
}

.ptz-down {
	top: 100px;
	left: 50px;
}

.ptz-zoomin {
	top: 150px;
	left: 20px;
}

.ptz-zoomout {
	top: 150px;
	left: 80px;
}

.ptz-up,
.ptz-left,
.ptz-right,
.ptz-down,
.ptz-center > .fa-microphone,
.ptz-zoomin,
.ptz-zoomout {
	cursor: pointer;

	&.readonly {
		cursor: auto;
	}
}
</style>

<style lang="less">
#dev-tree-player {
	.view-list {
        .video-show {
            .video {
                border: 1px solid #fff;

                &.active .video-inner {
                    border: 2px solid red;
                }
            }

            .col-sm-12 {
                &:nth-child(1) {
                    border: 0;
                }
            }

            .col-sm-6 {
                &:nth-child(1),
                &:nth-child(2) {
                    border-top-color: transparent;
                }

                &:nth-child(2),
                &:nth-child(4) {
                    border-right-color: transparent;
                }

                &:nth-child(1),
                &:nth-child(3) {
                    border-left-color: transparent;
                }

                &:nth-child(3),
                &:nth-child(4) {
                    border-bottom-color: transparent;
                }
            }
            .col-sm-4 {
                &:nth-child(1),
                &:nth-child(2),
                &:nth-child(3) {
                    border-top-color: transparent;
                }

                &:nth-child(3),
                &:nth-child(6),
                &:nth-child(9) {
                    border-right-color: transparent;
                }

                &:nth-child(7),
                &:nth-child(8),
                &:nth-child(9) {
                    border-bottom-color: transparent;
                }

                &:nth-child(1),
                &:nth-child(4),
                &:nth-child(7) {
                    border-left-color: transparent;
                }
            }

            .col-sm-3 {
                &:nth-child(1),
                &:nth-child(2),
                &:nth-child(3),
                &:nth-child(4) {
                    border-top-color: transparent;
                }

                &:nth-child(4),
                &:nth-child(8),
                &:nth-child(12),
                &:nth-child(16) {
                    border-right-color: transparent;
                }

                &:nth-child(13),
                &:nth-child(14),
                &:nth-child(15),
                &:nth-child(16) {
                    border-bottom-color: transparent;
                }

                &:nth-child(1),
                &:nth-child(5),
                &:nth-child(9),
                &:nth-child(13) {
                    border-left-color: transparent;
                }
            }
        }
    }
}
</style>
